#include "cpuinterface.h"
#include "cpucommand.h"
#include "cpugamedata.h"
#include "cpuminiondata.h"
#include "../GameEngine/minion.h"
#include "../GameEngine/engine.h"

CpuInterface::CpuInterface(QString &library_name)
{
    CpuLibrary.setFileName(library_name);
    if(CpuLibrary.load())
    {
        TickFunction = (LibTickPtr)CpuLibrary.resolve("MinionTick");
        if(TickFunction == nullptr)
            throw "Library is missing MinionTick().";

        InitFunction = (LibInitPtr)CpuLibrary.resolve("MinionInitialize");
        if(InitFunction == nullptr)
            throw "Library is missing MinionInitialize().";

        CpuTimer.setInterval(200);
        connect(&CpuTimer, SIGNAL(timeout()), this, SLOT(Tick()));
    }
    else throw "Failed to load library.";
}

void CpuInterface::CopyMyMinion()
{
    GameInfo.MyMinion.Armor = MyMinion->GetArmor();
    GameInfo.MyMinion.FireRate = MyMinion->GetFireRate();
    GameInfo.MyMinion.Health = MyMinion->GetHealth();
    GameInfo.MyMinion.MaxHealth = MyMinion->GetMaxHealth();
    GameInfo.MyMinion.Level = MyMinion->GetLevel();
    GameInfo.MyMinion.Orientation = MyMinion->GetOrientation();
    GameInfo.MyMinion.Power = MyMinion->GetPower();
    GameInfo.MyMinion.Range = MyMinion->GetRange();
    GameInfo.MyMinion.Sight = MyMinion->GetSight();
    GameInfo.MyMinion.Speed = MyMinion->GetSpeed();
    GameInfo.MyMinion.Team = MyMinion->GetTeam();
    GameInfo.MyMinion.X = MyMinion->GetX();
    GameInfo.MyMinion.Y = MyMinion->GetY();
}

void CpuInterface::GameStarted(int playerid, QPair<int,int> const &MapSize)
{
    CopyMyMinion();
    GameInfo.MapSize = MapSize;
    GameInfo.PlayerID = playerid;

    Minion *CurrentMinion = Engine::FirstMinion;
    while(CurrentMinion)
    {
        if(CurrentMinion->IsBase())
        {
            if(CurrentMinion->GetTeam() == MyMinion->GetTeam())
            {
                GameInfo.OwnBase = QPair<double,double>(CurrentMinion->GetX(), CurrentMinion->GetY());
            }
            else
            {
                GameInfo.EnemyBases.append(QPair<double,double>(CurrentMinion->GetX(), CurrentMinion->GetY()));
            }
        }
        CurrentMinion = CurrentMinion->GetNextMinion();
    }

    InitFunction(&GameInfo);

    CpuTimer.start();
}

void CpuInterface::GetVisibleObjects(QVector<MinionData> &VisibleObjects)
{
    Minion *CurrentMinion = Engine::FirstMinion;
    while(CurrentMinion)
    {
        if(Engine::IsVisible(CurrentMinion, MyMinion->GetTeam()))
        {
            MinionData object = {CurrentMinion->GetLevel(),
                                 CurrentMinion->GetX(),
                                 CurrentMinion->GetY(),
                                 CurrentMinion->GetOrientation(),
                                 CurrentMinion->GetTeam(),
                                 CurrentMinion->GetPower(),
                                 CurrentMinion->GetArmor(),
                                 CurrentMinion->GetHealth(),
                                 CurrentMinion->GetMaxHealth(),
                                 CurrentMinion->GetFireRate(),
                                 CurrentMinion->GetSight(),
                                 CurrentMinion->GetRange(),
                                 CurrentMinion->GetSpeed()};

            VisibleObjects.append(object);
        }

        CurrentMinion = CurrentMinion->GetNextMinion();
    }
}

void CpuInterface::Tick()
{
    QVector<MinionData> VisibleObjects;
    VisibleObjects.resize(ObjectType::Count);
    GetVisibleObjects(VisibleObjects);
    CopyMyMinion();

    CpuCommand command;

    TickFunction(GameInfo.PlayerID, VisibleObjects, command);

    if(command.Command != CMD_TYPE::NONE)
        NewOrder(command.X, command.Y);
}
